分类
联系方式
  1. 新浪微博
  2. E-mail

Dart eval:Compiler 类

介绍

Compiler 类负责将 Dart 代码转换为 dart_eval 特色的 EVC 字节码。该类提供两个方法,compile 和 compileSource。

成员

ctx 根作用域:

var ctx = CompilerContext(0);

源码的列表:

final additionalSources = <DartSource>[];

内置 Eval 插件:

final plugins = <EvalPlugin>[
  DartCorePlugin(),
  DartMathPlugin(),
  DartAsyncPlugin(),
];

其中:

  • DartCorePlugin:提供部分 dart:core 能力(Future、Duration、DateTime)
  • DartMathPlugin:提供部分 dart:math 能力
  • DartAsyncPlugin:提供部分 dart:async 能力

compile 方法

传入一组 Dart 代码进行编译,是 compileSources 的封装。函数签名:

Program compile(Map<String, Map<String, String>> packages) {
  final sources = packages
            .entries
            .expand((packageEntry) => packageEntry.value.entries
            .map((library) => DartSource(
                'package:${packageEntry.key}/${library.key}', 
                library.value)));
  return compileSources(sources);
}

其中,入参示例:

/// {
///   'package_name': {
///     'file_name1.dart': '''code''',
///     'file_name2.dart': '''code'''
///   }
/// }

按照包名-模块名-源代码 3 层维度。

具体的处理是:遍历包中每一个源代码,封装为 DartSource 对象:

  • 第一个参数是包名+模块名
  • 第二个参数是对应的源代码

_topLevelDeclarationsMap

签名:

var _topLevelDeclarationsMap = <int, Map<String, DeclarationOrBridge>>{};

顶层声明的映射列表:

  • key 的 int 是 libraryIndex,也就是说双维度下的第一个维度是 Library
  • value 是 Map,第二个维度是 Library 内的声明
    • Key 是 String 声明的名称,有 dart_eval 定义的简单协议
    • Value 是 DeclarationOrBridge:对应的 AST 语法节点

相关方法:

compileSources 方法

该方法执行具体的编译工作。具体代码如下:

/// Compile a unit set of Dart code into a program
Program compileSources([Iterable<DartSource> sources = const [], bool debugPerf = true]) {
  _topLevelDeclarationsMap = <int, Map<String, DeclarationOrBridge>>{};
  _topLevelGlobalIndices = <int, Map<String, int>>{};
  _instanceDeclarationsMap = <int, Map<String, Map<String, Declaration>>>{};
  
  // 创建一个作用域
  ctx = CompilerContext(0);

  //……

  // 遍历 sources(附加上 additionalSources)
  // units 为所有代码编译后的抽象语法树,类型为 List<DartCompilationUnit>
  final units = sources.followedBy(additionalSources).map((source) {
    //……
    //加载源代码,如果代码位于文件中,将读取磁盘
    //在内部调用 dart analyzer 的 parseString 方法,将源码转换为抽象语法树,得到 CompilationUnit
    //再对 CompilationUnit 进行解析,最后转化为 DartCompilationUnit 实体
    final parsed = cachedParsedSources[source] = source.load();
    return parsed;
  }).toList();

  //……

  // 类型为 Set<Library>,将所有源码解析为 Library
  // Library 为 dart_eval 提供的实体,表示一个 Library
  // 在 _buildLibraries 中解析类之间的引用关系(后文分析)
  final unitLibraries = {
    ..._buildLibraries(units),
  };

  // 建立 uri 到 Library 的映射关系,for 还能在 Map 中这么使用
  final unitLibraryUriMap = {for (final library in unitLibraries) library.uri: library};
  
  // 对源码 library 和桥接 library 的合并
  final libraries = <Library>{};
  // 记录同时提供源码和桥接的 library
  final mergedLibraryUris = <Uri>{};

  // 桥接库
  for (final bridgeLibrary in _bridgeDeclarations.keys) {
    /// 创建桥接库声明列表,List<DeclarationOrBridge>
    final bridgeLibDeclarations = [
      // _bridgeDeclarations 类型为 <String, List<BridgeDeclaration>>
      // key 是对应的 library,value 是所有的声明
      for (final bridgeDeclaration in _bridgeDeclarations[bridgeLibrary]!)
        // DeclarationOrBridge 和 BridgeDeclaration 不是一个类
        DeclarationOrBridge(-1, bridge: bridgeDeclaration)
    ];

    /// 桥接库 url
    final uri = Uri.parse(bridgeLibrary);
    
    // 看传入的代码里面,有没有 bridgeLibrary
    // 如果两者有重合,进行合并操作
    final unitLibrary = unitLibraryUriMap[uri];
    if (unitLibrary != null) { // 重合
      /// declarations 中将源码实现与桥接实现合二为一
      libraries.add(unitLibrary.copyWith(declarations: [...unitLibrary.declarations, ...bridgeLibDeclarations]));
      /// 记录这是一个 mergedLibrary
      mergedLibraryUris.add(uri);
    } else {
      // 如果两者没重合,根据 bridgeLibrary 创建一个 Library
      libraries.add(Library(Uri.parse(bridgeLibrary), imports: [], exports: [], declarations: [
        for (final bridgeDeclaration in _bridgeDeclarations[bridgeLibrary]!)
          DeclarationOrBridge(-1, bridge: bridgeDeclaration)
      ]));
    }
  }

  // 将纯源码 library 添加到 libraries 中
  unitLibraryUriMap.forEach((uri, library) {
    if (!mergedLibraryUris.contains(uri)) {
      libraries.add(library);
    }
  });

  var i = 0;
  final libraryIndexMap = <Library, int>{};
  
  // 解析库的 export 和 import 关系
  // 通过 libraryIndexMap 记录了一个索引
  final visibleDeclarations =
      _resolveImportsAndExports(
          libraries, 
          (library) => libraryIndexMap[library] ?? (libraryIndexMap[library] = i++));

  // 又进行了一步操作,没太看懂
  for (final library in libraries) {
    final libraryIndex = libraryIndexMap[library] ?? (libraryIndexMap[library] = i++);
    for (final declarationOrBridge in library.declarations) {
      _populateLookupTablesForDeclaration(libraryIndex, declarationOrBridge);
    }
  }

  // 将 library 和源码的映射传入作用域
  final libraryMapString = {for (final entry in libraryIndexMap.entries) entry.key.uri.toString(): entry.value};
  ctx.libraryMap = libraryMapString;

  // 
  final visibleDeclarationsByIndex = {
    for (final lib in libraries) libraryIndexMap[lib]!: {...visibleDeclarations[lib]!}
  };

  final declarationTypes = <DeclarationOrBridge, TypeRef>{};

  for (final library in libraries) {
    final libraryIndex = libraryIndexMap[library]!;
    for (final declaration in library.declarations) {
      final type = _cacheTypeRef(libraryIndex, declaration);
      if (type != null) {
        declarationTypes[declaration] = type;
      }
    }
  }

  final visibleTypesByIndex = <int, Map<String, TypeRef>>{};
  for (final library in libraries) {
    final libraryIndex = libraryIndexMap[library]!;
    final declarations = visibleDeclarations[library]!;

    for (final entry in declarations.entries) {
      final name = entry.key;
      final dop = entry.value;
      if (dop.children != null) {
        final res = <String, TypeRef>{};
        for (final childName in dop.children!.keys) {
          final child = dop.children![childName]!;
          final _cached = declarationTypes[child];
          if (_cached == null) continue;
          res['$name.$childName'] = _cached;
          if (child.isBridge) {
            final bridge = child.bridge!;
            if (bridge is BridgeClassDef) {
              child.bridge =
                  bridge.copyWith(type: bridge.type.copyWith(type: BridgeTypeRef.type(ctx.typeRefIndexMap[_cached])));
            } else if (bridge is BridgeEnumDef) {
              child.bridge = bridge.copyWith(type: BridgeTypeRef.type(ctx.typeRefIndexMap[_cached]));
            } else {
              assert(false);
            }
          }
        }
        visibleTypesByIndex[libraryIndex] ??= {...coreDeclarations};
        visibleTypesByIndex[libraryIndex]!.addAll(res);
        continue;
      }
      visibleTypesByIndex[libraryIndex] ??= {...coreDeclarations};
      final declarationOrBridge = dop.declaration!;
      final type = declarationTypes[declarationOrBridge];
      if (type == null) continue;
      if (declarationOrBridge.isBridge) {
        final bridge = declarationOrBridge.bridge!;
        if (bridge is BridgeClassDef) {
          declarationOrBridge.bridge =
              bridge.copyWith(type: bridge.type.copyWith(type: BridgeTypeRef.type(ctx.typeRefIndexMap[type])));
        } else if (bridge is BridgeEnumDef) {
          declarationOrBridge.bridge = bridge.copyWith(type: BridgeTypeRef.type(ctx.typeRefIndexMap[type]));
        } else {
          assert(false);
        }
      }
      visibleTypesByIndex[libraryIndex]![name] = type;
    }
  }

  ctx.topLevelDeclarationsMap = _topLevelDeclarationsMap;
  ctx.instanceDeclarationsMap = _instanceDeclarationsMap;
  ctx.visibleDeclarations = visibleDeclarationsByIndex;
  ctx.visibleTypes = visibleTypesByIndex;

  for (final library in libraries) {
    final libraryIndex = libraryIndexMap[library]!;
    for (final dec in library.declarations) {
      if (dec.isBridge) {
        final bridge = dec.bridge;
        if (bridge is BridgeClassDef) {
          _assignBridgeStaticFunctionIndicesForClass(bridge);
        } else if (bridge is BridgeEnumDef) {
          _assignBridgeGlobalValueIndicesForEnum(bridge);
        } else if (bridge is BridgeFunctionDeclaration) {
          _assignBridgeStaticFunctionIndicesForFunction(libraryIndex, bridge);
        }
      }
    }
  }

  ctx.topLevelGlobalIndices = _topLevelGlobalIndices;

  try {
    /// Compile statics first so we can infer their type
    _topLevelDeclarationsMap.forEach((key, value) {
      value.forEach((lib, _declaration) {
        if (_declaration.isBridge) {
          return;
        }
        final declaration = _declaration.declaration!;
        ctx.library = key;
        if (declaration is VariableDeclaration && declaration.parent!.parent is TopLevelVariableDeclaration) {
          compileDeclaration(declaration, ctx);
          ctx.resetStack();
        } else if (declaration is ClassDeclaration) {
          ctx.currentClass = declaration;
          for (final d in declaration.members.whereType<FieldDeclaration>().where((e) => e.isStatic)) {
            compileFieldDeclaration(-1, d, ctx, declaration);
            ctx.resetStack();
          }
          ctx.currentClass = null;
        }
      });
    });

    /// Compile the rest of the declarations
    _topLevelDeclarationsMap.forEach((key, value) {
      ctx.topLevelDeclarationPositions[key] = {};
      ctx.instanceDeclarationPositions[key] = {};
      value.forEach((lib, _declaration) {
        if (_declaration.isBridge) {
          return;
        }
        final declaration = _declaration.declaration!;
        if (declaration is ConstructorDeclaration ||
            declaration is MethodDeclaration ||
            declaration is VariableDeclaration) {
          return;
        }
        ctx.library = key;
        compileDeclaration(declaration, ctx);
        ctx.resetStack();
      });
    });
  } on CompileError catch (e) {
    throw e.copyWithContext(ctx);
  }

  for (final library in libraries) {
    for (final dec in library.declarations) {
      if (dec.isBridge) {
        final bridge = dec.bridge;
        if (bridge is BridgeClassDef && bridge.bridge) {
          _reassignBridgeStaticFunctionIndicesForClass(bridge);
        }
      }
    }
  }

  for (final type in ctx.runtimeTypeList) {
    ctx.typeTypes.add(type.resolveTypeChain(ctx).getRuntimeIndices(ctx));
  }

  final globalInitializers = List<int>.filled(ctx.globalIndex, 0);

  for (final gi in ctx.runtimeGlobalInitializerMap.entries) {
    globalInitializers[gi.key] = gi.value;
  }

  return Program(
      ctx.topLevelDeclarationPositions,
      ctx.instanceDeclarationPositions,
      ctx.typeNames,
      ctx.typeTypes,
      ctx.offsetTracker.apply(ctx.out),
      libraryMapString,
      ctx.bridgeStaticFunctionIndices,
      ctx.constantPool.pool,
      ctx.runtimeTypes.pool,
      globalInitializers,
      ctx.enumValueIndices);
}

_buildLibraries 方法

先看该方法的函数签名:

List<Library> _buildLibraries(Iterable<DartCompilationUnit> units) {

可以看出,它负责将一组 DartCompilationUnit 转换为一组 Library。

在《Dart eval:DartCompilationUnit 实体类》有介绍:该类表示一个经过解析后的 Dart 源文件(AST),但是尚未被解析为库(library)。

_buildLibraries 方法则完成源代码到 Library 的最终转化。

具体代码实现如下:

List<Library> _buildLibraries(Iterable<DartCompilationUnit> units) {
  /// 自增的 id 生成器,每个 Library 都有一个唯一整型 Id 与之对应
  var i = 0;
  /// id-DartCompilationUnit 映射
  final compilationUnitMap = <int, DartCompilationUnit>{};
  /// url-id 映射
  final uriMap = <String, int>{};
  /// library-id 映射
  final libraryIdMap = <String, int>{};

  for (final unit in units) {
    /// 建立映射关系
    compilationUnitMap[i] = unit;
    uriMap[unit.uri.toString()] = i;
    if (unit.library != null) {
      /// Library 指令,有的源码开头的 "library ******"
      libraryIdMap[unit.library!.name.name] = i;
    }
    /// id 自增
    i++;
  }

  // CompilationUnit 图结构
  final cuGraph = CompilationUnitGraph(compilationUnitMap, uriMap, libraryIdMap);
  // 计算强链接组件,基于 Dijkstra Path-based_strong_component_algorithm 算法
  // 返回类型 List<List<T>>,内层列表应该是 id 列表
  final libGroups = computeStrongComponents(cuGraph);

  final libraries = <Library>[];
  /// 第一层遍历针对有 part of 的场景,多个文件组成一个 Library
  for (final group in libGroups) { /// 第一层 List 是 Group
    // 找出主文件 ID
    final primaryId = group.length == 1 ? group[0] : group.firstWhere((e) => compilationUnitMap[e]!.partOf == null);
    // 主文件
    final primary = compilationUnitMap[primaryId]!;
    // 创建 Library
    final library = Library(
        primary.uri,
        library: primary.library?.name.name,
        imports: primary.imports,
        exports: primary.exports,
        declarations: group /// 将 group 中所有 declarations 转为 DeclarationOrBridge
            .map((e) => compilationUnitMap[e]!)
            .fold(
                [], // 初始值
                // pv:保存值,element:当前元素
                // .. 是添加完元素再把 pv 返回去
                (pv, element) => pv..addAll(
                    element.declarations.map(
                        (d) => DeclarationOrBridge(-1, declaration: d)))));
    libraries.add(library);
  }

  return libraries;
}

如果把 Dijkstra 当作一个黑盒的话,整体逻辑并不复杂。作为黑盒的 Dijkstra 算法,主要是:有的 Dart 代码有多个文件构成(通过 part、part of 语法),这里将这种代码的 part 和主题汇集到一个 group 中去。

组后,该方法可以简单认为是,Dart 中源文件就是一个 Library,完成从 DartCompilationUnit 到 Library 的最终转化。

_expandDeclarations 方法

方法签名:

Iterable<Pair<String, DeclarationOrBridge>> _expandDeclarations(
    List<DeclarationOrBridge> declarations) sync* {

是一个生成器方法,该方法传入的 declarations 是一个 Library 的 declarations。 该方法的作用是:declarations 表示声明,声明内部可能还有声明(如类声明内部包括成员声明),给方法将其全部解析打平,结果的 Pair 的 key 是打平后声明的唯一表示,Value 是对应的声明实体 DeclarationOrBridge。

Iterable<Pair<String, DeclarationOrBridge>> _expandDeclarations(List<DeclarationOrBridge> declarations) sync* {
  /// 遍历声明
  for (final d in declarations) {
    if (d.isBridge) { /// 处理桥接声明
      final bridge = d.bridge as BridgeDeclaration;
      /// 只不过是把名字按照不同类型处理了一下,第二参数还是 DeclarationOrBridge
      if (bridge is BridgeClassDef) {                   /// 声明一个类
        /// 桥接类名称
        final name = bridge.type.type.spec!.name;
        yield Pair(name, d);
      } else if (bridge is BridgeEnumDef) {             /// 声明一个枚举
        /// 桥接枚举名称
        final name = bridge.type.spec!.name;
        yield Pair(name, d);
      } else if (bridge is BridgeFunctionDeclaration) { /// 声明一个桥接函数
        /// 这个简单,直接就是函数名称
        yield Pair(bridge.name, d);
      }
    } else { /// 如果是源码声明
      final declaration = d.declaration!;
      if (declaration is NamedCompilationUnitMember) {
        final dName = declaration.name2.value() as String;
        /// 先把当前发出去
        yield Pair(dName, d);
        /// 如果是类声明
        if (declaration is ClassDeclaration) {
          /// 再把类里面的成员也发出去
          /// 类成员类型包括:ConstructorDeclaration、MethodDeclaration、MethodDeclaration
          for (final member in declaration.members) {
            if (member is ConstructorDeclaration) {
              yield Pair('$dName.${member.name2?.value() ?? ""}', DeclarationOrBridge(-1, declaration: member));
            } else if (member is MethodDeclaration && member.isStatic) {
              yield Pair('$dName.${member.name2.value()}', DeclarationOrBridge(-1, declaration: member));
            } else if (member is MethodDeclaration && member.isStatic) {
              yield Pair('$dName.${member.name2.value()}', DeclarationOrBridge(-1, declaration: member));
            }
          }
        }
      } else if (declaration is TopLevelVariableDeclaration) { /// 顶层变量声明
        for (final v in declaration.variables.variables) {
          yield Pair(v.name2.value() as String, DeclarationOrBridge(-1, declaration: v));
        }
      }
    }
  }
}

_resolveImportsAndExports 方法

先看看方法签名:

Map<Library, Map<String, DeclarationOrPrefix>> _resolveImportsAndExports(
                                                        Iterable<Library> libraries, 
                                                        int Function(Library) resolveLibraryId) {

传入参数:

  • libraries 列表
  • 一个函数,用于解析 library 的 id

返回值:一个 Map:

  • key 是 library
  • value 又是一个 Map,这里面的内容不太清晰
    • key 是字符串
    • value 是 DeclarationOrPrefix

外面实际调用时,传入的函数长这样:

(library) => libraryIndexMap[library] ?? (libraryIndexMap[library] = i++)

可以看到,在 compileSource 中,还有一个基于自增的整型 id 生成器。 下面来看内部具体逻辑:

Map<Library, Map<String, DeclarationOrPrefix>> _resolveImportsAndExports(
    Iterable<Library> libraries, 
    int Function(Library) resolveLibraryId) {

  /// uri-Library 映射
  final uriMap = {for (final l in libraries) l.uri: l};

  /// 基于 library 的 export 构成的有向图
  final exportGraph = DirectedGraph<Uri>(
    // 传入参数是个 Map,表示 edges  
    // 遍历 libraries,key 是 url,value 是一个 Set,是它对外的 export
    {
      for (final l in libraries) l.uri: {for (final export in l.exports) Uri.parse(export.uri.stringValue!)}
    }
  );

  final result = <Library, Map<String, DeclarationOrPrefix>>{};

  // 遍历 libraries
  for (final l in libraries) {
    // 该 Library 下所有的可见声明
    final _visibleDeclarations = <String, DeclarationOrPrefix>{
      for (final d in _expandDeclarations(l.declarations))
        // Key:声明的标识 value:DeclarationOrPrefix(声明内容,并记忆对应 library 的 id)
        d.first: DeclarationOrPrefix(declaration: d.second..sourceLib = resolveLibraryId(l)),
    };

    final dartCoreUri = Uri.parse('dart:core');
    final isDartCore = l.uri == dartCoreUri;

    for (final import in [
      /// 取出库的所有 imports 做映射
      ...l.imports.map( /// e 类型是 ImportDirective
          (e) => _Import( /// _Import 是一个类
                         Uri.parse(e.uri.stringValue!), 
                         e.prefix?.name,   /// as 某某
                         e.combinators)),  /// show 某某 或者 hide 某某
      if (!isDartCore) _Import(dartCoreUri, null) // DartCore 单独处理,如果有的话拼在后面
    ]) {
      /// 使用了 directed_graph 
      /// 返回一个以 import.uri 为根的树状结构,类型为 List<Set<T>>
      final tree = exportGraph.crawler.tree(import.uri);
      /// 
      final importedLibs = [...tree.expand((e) => e), import.uri]
          .map((e) => uriMap[e] ?? (throw CompileError("Cannot find import '$e' (while parsing '${l.uri}')")))
          .toSet();
      final importedExports = importedLibs.map((e) => e.exports).expand((e) => e);

      final exportsPerUri = <Uri, List<ExportDirective>>{};
      for (final export in importedExports) {
        final uriList = exportsPerUri[export.uri.stringValue!];
        if (uriList != null) {
          uriList.add(export);
        } else {
          exportsPerUri[Uri.parse(export.uri.stringValue!)] = [export];
        }
      }

      final validImport = (String name) {
        if (name.startsWith('_')) return false;
        if (import.combinators.isEmpty) {
          return true;
        }
        for (final combinator in import.combinators) {
          if (combinator is ShowCombinator) {
            if ({for (final n in combinator.shownNames) n.name}.contains(name)) {
              return true;
            }
            return false;
          } else if (combinator is HideCombinator) {
            if ({for (final n in combinator.hiddenNames) n.name}.contains(name)) {
              return false;
            }
            return true;
          }
          throw CompileError('Unsupported import combinator ${combinator.runtimeType} (while parsing ${l.uri})');
        }
        return false;
      };

      final visibleDeclarations = {
        for (final lib in importedLibs)
          for (final declaration in _expandDeclarations(lib.declarations).where((e) => validImport(e.first))) ...{
            if (lib.uri == import.uri) declaration,
            for (final export in exportsPerUri[lib.uri] ?? [])
              if (export.combinators.isEmpty)
                declaration
              else
                for (final combinator in export.combinators)
                  if (combinator is ShowCombinator) ...{
                    if ({for (final n in combinator.shownNames) n.name}.contains(declaration.first)) declaration
                  } else if (combinator is HideCombinator) ...{
                    if (!({for (final n in (combinator).hiddenNames) n.name}.contains(declaration.first))) declaration
                  }
          }
      };

      final mappedVisibleDeclarations = {
        if (import.prefix != null)
          import.prefix!: DeclarationOrPrefix(children: {for (final d in visibleDeclarations) d.first: d.second})
        else
          for (final d in visibleDeclarations) d.first: DeclarationOrPrefix(declaration: d.second)
      };

      _visibleDeclarations.addAll(mappedVisibleDeclarations);
    }

    result[l] = _visibleDeclarations;
  }

  return result;
}

对 exportGraph 的理解